{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "6ff43526",
   "metadata": {},
   "source": [
    "# Exploring Generative Methodologies with Deepchem\n",
    "\n",
    "By Debasish Mohanty\n",
    "\n",
    "# Table of Contents:\n",
    "1. [Introduction](#introduction)\n",
    "2. [Generative Methods](#genmethods)\n",
    "      - [BRICS](#brics)\n",
    "      - [Neural Networks](#nns)\n",
    "3. [Code Implementation with Deepchem](#codeimplement)\n",
    "      - [Setup](#setup)\n",
    "      - [BRICS Generation with SMILES](#bricssmiles)\n",
    "      - [BRICS Generation with PSMILES](#bricspsmiles)\n",
    "      - [LSTM Generators](#lstmimplement)\n",
    "      - [Pretrained Implementation with PSMILES and weighted graphs](#pretrainedpsmileswdg)\n",
    "4. [Summary](#sum)\n",
    "5. [References](#ref)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a0f02e3f",
   "metadata": {},
   "source": [
    "## Colab\n",
    "\n",
    "This tutorial is designed to be done in Google colab. If you'd like to open this notebook in colab, you can use the following link.\n",
    "\n",
    "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/deepchem/deepchem/blob/master/examples/tutorials/Exploring_Generative_Methodologies_with_Deepchem.ipynb)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b45aa5f2",
   "metadata": {},
   "source": [
    "# 1. Introduction <a class=\"anchor\" id=\"introduction\"></a>\n",
    "\n",
    "The development of new chemical compounds and materials, especially organic molecules and polymers, often relies on computational techniques for molecular design and discovery. Generative modeling in cheminformatics is a powerful approach that leverages various algorithms to predict and create novel molecules. In this tutorial, we will delve into the application of generative methodologies using the Deepchem library, focusing on fragment-based and deep learning-driven techniques for molecular generation. We begin by exploring BRICS (Breaking of Retrosynthetically Interesting Chemical Substructures), a method for systematically fragmenting and reconstructing molecular structures based on chemically meaningful subunits. BRICS offers a rule-based approach that respects chemical stability and common retrosynthetic pathways, making it useful for creating feasible molecular candidates.\n",
    "\n",
    "Next, we explore neural network-based methods, particularly recurrent neural networks (RNNs) like Long Short-Term Memory (LSTM), which have been widely used for sequence generation. In the context of molecular generation, LSTM models are trained to produce novel molecules by learning from patterns in SMILES (Simplified Molecular Input Line Entry System) representations of known compounds. This method excels in capturing complex structural patterns and generating novel molecules that fit within the chemical space learned from the training data. We also extend these methods to Polymer SMILES (PSMILES), a notation that represents repeating polymer structures, making it possible to model polymers using the same generative techniques. By incorporating weighted graphs and pretrained models, we enhance the model's ability to generate polymers, pushing the boundaries of traditional molecular design. These generative and validation methods are explicitly utilized and analyzed in our research paper \"Open-source Polymer Generative Pipeline\". `[1]` This tutorial provides a comprehensive introduction to advanced molecular generative techniques, merging traditional rule-based methods with the flexibility of deep learning for chemical and polymer discovery."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c7e68ab8",
   "metadata": {},
   "source": [
    "# 2. Generative Methods <a class=\"anchor\" id=\"genmethods\"></a>\n",
    "\n",
    "Generative methods in computational chemistry have revolutionized the design and discovery of new molecules by enabling the generation of novel structures with specific properties. Techniques such as BRICS, Neural Networks, Variational Autoencoders (VAEs), and Transformer-based models offer a diverse range of approaches for molecular generation. BRICS, a rule-based method, assembles molecules from fragments, ensuring chemical validity and synthetic feasibility. Neural networks, including LSTMs, VAEs, and Transformer models, learn complex patterns in molecular data to generate new compounds. While VAEs allow interpolation in latent spaces to design smooth transitions between molecular properties, some models leverage attention mechanisms to capture long-range dependencies within molecular structures, offering fine control over molecular features. Furthermore, diffusion models have emerged as a powerful probabilistic approach for generating high-quality molecules by reversing a noise-adding process.\n",
    "\n",
    "However, for our purpose of exploring generative methodologies with Deepchem, we focus on BRICS and LSTM-based generators, which offer efficient and interpretable methods for fragment-based generation and sequential learning from molecular datasets, respectively. These methods are particularly well-suited for generating novel polymer structures and exploring chemical spaces in a controlled yet flexible manner, making them ideal for this tutorial.\n",
    "\n",
    "## BRICS <a class=\"anchor\" id=\"brics\"></a>\n",
    "\n",
    "BRICS (Breaking of Retrosynthetically Interesting Chemical Substructures) is a fragment-based generative method that assembles molecules by connecting predefined fragments. It is designed to mimic the way chemists think about breaking down molecules into smaller, chemically meaningful substructures. By combining these fragments, BRICS can generate novel molecular structures while ensuring synthetic feasibility. The strength of the BRICS method lies in its ability to generate chemically valid molecules that follow real-world synthesis rules, making it a useful tool for drug discovery and materials design. BRICS starts by decomposing molecules into fragments at specific, chemically significant bonds. It then combines these fragments in different ways to create new molecules. The method avoids chemically impossible or invalid bonds and ensures that the generated molecules adhere to certain synthetic constraints. For example we can use following case:\n",
    "\n",
    "Let's consider Styrene `(C6H5CH=CH2)` and Vinyl Acetate `(C=COC(C)=O)` as examples to illustrate the BRICS method.\n",
    "\n",
    "In the case of Styrene, the molecule can be fragmented into two key parts:\n",
    "\n",
    "- A benzene ring `(C6H5)`\n",
    "- A vinyl group `(CH=CH2)`\n",
    "\n",
    "For Vinyl Acetate `(C=COC(C)=O)`, the molecule can be fragmented into:\n",
    "\n",
    "- An acetoxy group `(*C(=O)C)`\n",
    "- An alkoxy vinyl group `(*OC=C)`\n",
    "\n",
    "The BRICS fragmentation method breaks the molecules at strategic points while retaining bond breakage information, represented as \"attachment points\" `(*)` that indicate where these fragments can be recombined.\n",
    "\n",
    "For instance, the benzene ring from Styrene can be recombined with the acetoxy group from Vinyl Acetate, resulting in a new hypothetical molecule where the benzene ring is connected to an acetoxy chain. Alternatively, the vinyl group from Styrene could be combined with the alkoxy vinyl group from Vinyl Acetate to form a novel structure. This recombination of molecular fragments in a controlled and logical way enables BRICS to generate a wide variety of potential new structures, providing a platform for exploring novel chemical compounds that could be of interest for further research or synthesis. The above process is illustrated in Figure-1.\n",
    "\n",
    "![brics-image](https://github.com/deepchem/deepchem/blob/master/examples/tutorials/assets/polymer_images/gen_tutorial_image.png?raw=1)\n",
    "\n",
    "*Figure - 1: BRICS method illustrated for styrene and vinly acetate*\n",
    "\n",
    "## Neural Networks <a class=\"anchor\" id=\"nns\"></a>\n",
    "\n",
    "Neural networks have significantly advanced the field of molecular generation by enabling the design of novel molecular structures based on learned patterns from large datasets. These techniques, which include Long Short-Term Memory (LSTM) networks, transformer-based models, and diffusion models, offer different advantages and challenges depending on the task at hand.\n",
    "\n",
    "LSTMs are a type of recurrent neural network (RNN) designed to handle sequential data, making them well-suited for generating molecular sequences such as SMILES (Simplified Molecular Input Line Entry System) strings. By learning the dependencies between atoms and bonds in a molecule, LSTMs can generate realistic molecules one atom or bond at a time. For instance, given the initial part of a SMILES string, an LSTM can predict the next characters, continuing the sequence until the molecule is fully described. In molecular generation tasks, LSTMs are valuable for their ability to learn from chemical datasets with sequential representation.\n",
    "\n",
    "LSTMs operate using gates, which control the flow of information within the network:\n",
    "\n",
    "1. **Forget Gate:** Decides which information from the previous time step should be discarded. It looks at the current input and the previous hidden state and outputs a value between 0 and 1. A value of 0 means \"forget\" and 1 means \"keep.\"\n",
    "\n",
    "2. **Input Gate:** Determines what new information should be added to the cell state. It uses the current input and the previous hidden state to decide which values to update in the memory.\n",
    "\n",
    "3. **Cell State:** The \"memory\" of the LSTM. The cell state is updated by the input and forget gates and carries information across time steps. This allows the LSTM to \"remember\" long-term dependencies and keep important information.\n",
    "\n",
    "4. **Output Gate:** Decides what the next hidden state should be based on the cell state and current input. The hidden state is then passed to the next time step and also used for the output prediction.\n",
    "\n",
    "These gates allow the LSTM to retain important information over long sequences while ignoring irrelevant data, which helps overcome the vanishing gradient problem often encountered in traditional RNNs. In molecular generation, an LSTM network can be trained on molecular data (such as SMILES strings) to learn patterns of atoms, bonds, and functional groups. The LSTM can then generate new molecules by predicting the next character in the sequence, one step at a time, building up a full SMILES string. This sequential nature makes LSTMs particularly effective for generating molecules in an iterative process, with each predicted atom or bond influencing the next, ensuring that generated molecules are coherent and chemically valid"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fe53351e",
   "metadata": {},
   "source": [
    "## Code Implementation with Deepchem <a class=\"anchor\" id=\"codeimplement\"></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "685dac18",
   "metadata": {},
   "source": [
    "## Setup <a class=\"anchor\" id=\"setup\"></a>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9597d7b8",
   "metadata": {},
   "outputs": [],
   "source": [
    "# installation\n",
    "! pip install rdkit deepchem[torch]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "85be8235-03ca-4aa8-adaa-65d3cb5b2051",
   "metadata": {},
   "outputs": [],
   "source": [
    "# all imports\n",
    "from deepchem.models.torch_models import LSTMGenerator\n",
    "from deepchem.models import BRICSGenerator\n",
    "from deepchem.utils.poly_wd_graph_utils import PolyWDGStringValidator\n",
    "from deepchem.data import NumpyDataset\n",
    "import torch\n",
    "from rdkit import Chem"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "40229667",
   "metadata": {},
   "source": [
    "## BRICS Generation with SMILES <a class=\"anchor\" id=\"bricssmiles\"></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9e7b7c27",
   "metadata": {},
   "source": [
    "DeepChem integrates BRICS functionality through the `BRICSGenerator` class. This class manages the underlying processes and generates unique candidate molecules, providing the results via a straightforward method. The features are outlined below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "30c5eab9",
   "metadata": {},
   "outputs": [],
   "source": [
    "sample_smiles = ['CC(=O)Oc1ccccc1C(=O)O', 'CC(=O)NC1=CC=C(O)C=C1']\n",
    "brics_generator = BRICSGenerator()\n",
    "generated_candidates, number_of_candidates = brics_generator.sample(sample_smiles)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "d19367c8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 . O=C(O)c1ccc(O)cc1\n",
      "2 . O=C(O)c1ccccc1-c1ccc(O)cc1\n",
      "3 . O=C(O)c1ccccc1C(=O)O\n",
      "4 . CC(=O)Nc1ccccc1C(=O)O\n",
      "5 . O=C(O)c1ccccc1Nc1ccc(O)cc1\n",
      "6 . O=C(O)c1ccccc1Nc1ccccc1-c1ccc(O)cc1\n",
      "7 . O=C(O)c1ccccc1Nc1ccccc1C(=O)O\n",
      "8 . O=C(O)c1ccccc1-c1ccccc1-c1ccc(O)cc1\n",
      "9 . O=C(O)c1ccccc1-c1ccccc1C(=O)O\n",
      "10 . CC(=O)Nc1ccccc1-c1ccccc1C(=O)O\n",
      "11 . O=C(O)c1ccccc1-c1ccccc1Nc1ccc(O)cc1\n",
      "12 . CC(=O)Oc1ccccc1-c1ccccc1C(=O)O\n",
      "13 . O=C(O)c1ccccc1-c1ccccc1Oc1ccc(O)cc1\n",
      "14 . O=C(O)c1ccccc1-c1ccccc1-c1ccccc1C(=O)O\n",
      "15 . O=C(O)c1ccccc1-c1ccccc1-c1ccccc1-c1ccc(O)cc1\n",
      "16 . CC(=O)Oc1ccccc1C(=O)O\n",
      "17 . O=C(O)c1ccccc1Oc1ccc(O)cc1\n",
      "18 . O=C(O)c1ccccc1Oc1ccccc1C(=O)O\n",
      "19 . O=C(O)c1ccccc1Oc1ccccc1-c1ccc(O)cc1\n",
      "20 . CC(=O)OC(C)=O\n",
      "21 . CC(=O)Oc1ccc(O)cc1\n",
      "22 . CC(=O)Oc1ccccc1-c1ccc(O)cc1\n",
      "23 . CC(=O)Oc1ccccc1-c1ccccc1-c1ccc(O)cc1\n",
      "24 . CC(=O)Oc1ccccc1OC(C)=O\n",
      "25 . CC(=O)Oc1ccccc1Oc1ccc(O)cc1\n",
      "26 . CC(=O)Oc1ccccc1Nc1ccc(O)cc1\n",
      "27 . CC(=O)Nc1ccccc1OC(C)=O\n",
      "28 . Oc1ccc(-c2ccccc2Oc2ccccc2-c2ccc(O)cc2)cc1\n",
      "29 . Oc1ccc(Nc2ccccc2Oc2ccc(O)cc2)cc1\n",
      "30 . CC(=O)Nc1ccccc1Oc1ccc(O)cc1\n",
      "31 . Oc1ccc(Oc2ccccc2Oc2ccc(O)cc2)cc1\n",
      "32 . Oc1ccc(Oc2ccccc2-c2ccc(O)cc2)cc1\n",
      "33 . Oc1ccc(Oc2ccccc2-c2ccccc2-c2ccc(O)cc2)cc1\n",
      "34 . Oc1ccc(Oc2ccc(O)cc2)cc1\n",
      "35 . CC(=O)Nc1ccc(O)cc1\n",
      "36 . CC(=O)NC(C)=O\n",
      "37 . CC(=O)Nc1ccccc1-c1ccc(O)cc1\n",
      "38 . CC(=O)Nc1ccccc1-c1ccccc1-c1ccc(O)cc1\n",
      "39 . CC(=O)Nc1ccccc1NC(C)=O\n",
      "40 . CC(=O)Nc1ccccc1Nc1ccc(O)cc1\n",
      "41 . Oc1ccc(Nc2ccccc2-c2ccccc2-c2ccc(O)cc2)cc1\n",
      "42 . Oc1ccc(-c2ccccc2-c2ccccc2-c2ccc(O)cc2)cc1\n",
      "43 . Oc1ccc(-c2ccccc2-c2ccccc2-c2ccccc2-c2ccc(O)cc2)cc1\n",
      "44 . Oc1ccc(-c2ccccc2-c2ccc(O)cc2)cc1\n",
      "45 . Oc1ccc(Nc2ccccc2-c2ccc(O)cc2)cc1\n",
      "46 . Oc1ccc(-c2ccccc2Nc2ccccc2-c2ccc(O)cc2)cc1\n",
      "47 . Oc1ccc(Nc2ccccc2Nc2ccc(O)cc2)cc1\n",
      "48 . Oc1ccc(Nc2ccc(O)cc2)cc1\n",
      "49 . Oc1ccc(-c2ccc(O)cc2)cc1\n"
     ]
    }
   ],
   "source": [
    "for i, candidate in enumerate(generated_candidates):\n",
    "    print(i+1,\".\", candidate)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d1d14f4a",
   "metadata": {},
   "source": [
    "In the above example we can observe generation of 49 new candidates from two smiles input."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "716aad9f",
   "metadata": {},
   "source": [
    "## BRICS Generation with PSMILES <a class=\"anchor\" id=\"bricspsmiles\"></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1ecb6d19",
   "metadata": {},
   "source": [
    "The `BRICSGenerator` class by default takes smiles as input. But we have extended the application for psmiles and dendrimers (having more than two endpoints[`*`]) as well. Those features can be used as follows."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "ca981e8e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# with polymer application\n",
    "sample_psmiles = ['*CC(=O)CC*', '*c1ccc(CC*)cc1', '*CC(CC*)CC*']\n",
    "# is_polymer determines whether the input is a polymer or not  \n",
    "psmiles_generated_candidates, ps_number_of_candidates = brics_generator.sample(sample_psmiles, is_polymer=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "3368ecc3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 . [*]CCc1ccc([*])cc1\n",
      "2 . [*]c1ccc(-c2ccc([*])cc2)cc1\n"
     ]
    }
   ],
   "source": [
    "for i, candidate in enumerate(psmiles_generated_candidates):\n",
    "    print(i+1,\".\", candidate)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3f19a0eb",
   "metadata": {},
   "source": [
    "In the above example, we can observe 3 psmiles used to generate two unique psmiles candidates."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "c1da03cf",
   "metadata": {},
   "outputs": [],
   "source": [
    "# with dendrimer application\n",
    "sample_dendrimers_source = [\n",
    "        # Branched aliphatic cores\n",
    "        '*C(=O)N(CC(=O)*)CC(=O)*',  # 3-point N-centered with amide bonds\n",
    "        '*OC(=O)c1c(C(=O)O*)cc(C(=O)O*)cc1',  # 3-point aromatic with ester bonds\n",
    "    ]\n",
    "dendrimer_generated_candidates, den_number_of_candidates = brics_generator.sample(sample_dendrimers_source, is_polymer=True, is_dendrimer=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "194e0701",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 . O=C([*])CO[*]\n",
      "2 . O=C([*])O[*]\n",
      "3 . O=C(O[*])c1cc(O[*])ccc1O[*]\n",
      "4 . [*]Oc1ccc(O[*])c(O[*])c1\n",
      "5 . O=C(O[*])c1ccc(O[*])c(O[*])c1\n",
      "6 . O=C(O[*])c1ccc(O[*])cc1O[*]\n",
      "7 . O=C([*])N(C(=O)[*])C(=O)[*]\n",
      "8 . O=C([*])CN(C(=O)[*])C(=O)[*]\n",
      "9 . O=C([*])CN(CC(=O)[*])C(=O)[*]\n",
      "10 . O=C([*])CN(CC(=O)[*])CC(=O)[*]\n"
     ]
    }
   ],
   "source": [
    "for i, candidate in enumerate(dendrimer_generated_candidates):\n",
    "    print(i+1,\".\", candidate)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c1019c2c",
   "metadata": {},
   "source": [
    "In the above example, we can observe generation of 10 candidates in which 2 are psmiles for polymers while other 8 can be described as dendrimers."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cacb660e",
   "metadata": {},
   "source": [
    "## LSTM Generators <a class=\"anchor\" id=\"lstmimplement\"></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "34f7da31",
   "metadata": {},
   "source": [
    "DeepChem provides a versatile LSTM generator that learns sequential string patterns from its input, making it compatible with various string representations such as SMILES, PSMILES, and weighted graphs. The `LSTMGenerator` class is used for both training and inference of the LSTM model. The following example demonstrates its application."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "c274bf91",
   "metadata": {},
   "outputs": [],
   "source": [
    "# training the model\n",
    "generator = LSTMGenerator(model_dir=\"./lstm_generator_model\")\n",
    "dataset = NumpyDataset([\"CCC\"])\n",
    "loss = generator.fit(dataset,\n",
    "                        nb_epoch=3,\n",
    "                        checkpoint_interval=1,\n",
    "                        max_checkpoints_to_keep=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "dcbb30b8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "loss >>> 10.016533851623535\n"
     ]
    }
   ],
   "source": [
    "print(\"loss >>>\", loss)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "96b544ed",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "generated >>> ['UnderworldEnricoyaGracepronunciationndingpuritychantedrecommendationsRadar']\n"
     ]
    }
   ],
   "source": [
    "# inference\n",
    "generated = generator.sample(1,max_len=10)\n",
    "print(\"generated >>>\", generated)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b0bdf9da",
   "metadata": {},
   "source": [
    "In the above example, we can see the random generation of string from the LSTM model which is trained only on one SMILES (i.e. `CCC`)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "2da67420",
   "metadata": {},
   "outputs": [],
   "source": [
    "restored_generator = LSTMGenerator()\n",
    "\n",
    "restored_generator.load_from_pretrained(source_model=LSTMGenerator(),\n",
    "                               model_dir=\"./lstm_generator_model\")\n",
    "random_gens = generator.sample(3, max_len=10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "3d89e1c8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "random generations >>> ['##lakedecreasefailsHoganSaskatchewanTrumanhoarsegigglelieShadow', '##vageDC00CouldchangedContemporaryLaurentOperatingwarsencourage', 'mythologynanttransformatainherentadministeredeuxstoredinformaloak']\n"
     ]
    }
   ],
   "source": [
    "print(\"random generations >>>\", random_gens)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b0cfaf3c",
   "metadata": {},
   "source": [
    "In above manner, we can load the model from a saved checkpoint to make the inference. "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7980433a",
   "metadata": {},
   "source": [
    "  As mentioned in the research paper we have trained two model of PSMILES (1 Million input data and 5 epochs) and weighted directed graphs (42 Thousands input data and 50 epochs) `[1]`. In the next section of the tutorial, we will be using those model checkpoints to make the inference. For more detail about the datasets and the training process go through our research paper \"Open-Source Polymer Generative Pipeline\". "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "43ae3e0e",
   "metadata": {},
   "source": [
    "## Pretrained Implementation with PSMILES and Weighted Directed Graphs <a class=\"anchor\" id=\"pretrainedpsmileswdg\"></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "edf6e14b",
   "metadata": {},
   "source": [
    "In the source research paper `[1]`, we have trained LSTM with PSMILES and weighted directed graphs (WDG) string representations. We have trained 1 million PSMILES datapoints for 5 epochs and 42 thousand WDG datapoints for 50 epochs. Those model checkpoints are used to generate following candidates. The model checkpoints can be loaded using following code block. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "36a7313c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--2025-02-21 21:17:24--  https://deepchemdata.s3.us-west-1.amazonaws.com/trained_models/PSMILES_LSTM_1M_5_epochs.pth\n",
      "Resolving deepchemdata.s3.us-west-1.amazonaws.com (deepchemdata.s3.us-west-1.amazonaws.com)... 16.15.4.244, 3.5.162.13, 52.219.117.138, ...\n",
      "Connecting to deepchemdata.s3.us-west-1.amazonaws.com (deepchemdata.s3.us-west-1.amazonaws.com)|16.15.4.244|:443... connected.\n",
      "HTTP request sent, awaiting response... 200 OK\n",
      "Length: 48343184 (46M) [binary/octet-stream]\n",
      "Saving to: ‘PSMILES_LSTM_1M_5_epochs.pth’\n",
      "\n",
      "PSMILES_LSTM_1M_5_e 100%[===================>]  46.10M  3.46MB/s    in 25s     \n",
      "\n",
      "2025-02-21 21:17:52 (1.84 MB/s) - ‘PSMILES_LSTM_1M_5_epochs.pth’ saved [48343184/48343184]\n",
      "\n",
      "--2025-02-21 21:17:53--  https://deepchemdata.s3.us-west-1.amazonaws.com/trained_models/WDGraph_LSTM_42K_50_epochs.pth\n",
      "Resolving deepchemdata.s3.us-west-1.amazonaws.com (deepchemdata.s3.us-west-1.amazonaws.com)... 52.219.112.49, 52.219.120.81, 52.219.113.170, ...\n",
      "Connecting to deepchemdata.s3.us-west-1.amazonaws.com (deepchemdata.s3.us-west-1.amazonaws.com)|52.219.112.49|:443... connected.\n",
      "HTTP request sent, awaiting response... 200 OK\n",
      "Length: 48343184 (46M) [binary/octet-stream]\n",
      "Saving to: ‘WDGraph_LSTM_42K_50_epochs.pth’\n",
      "\n",
      "WDGraph_LSTM_42K_50 100%[===================>]  46.10M  3.75MB/s    in 30s     \n",
      "\n",
      "2025-02-21 21:18:25 (1.55 MB/s) - ‘WDGraph_LSTM_42K_50_epochs.pth’ saved [48343184/48343184]\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# getting the pretrained model for LSTM with 1M PSMILES\n",
    "! wget https://deepchemdata.s3.us-west-1.amazonaws.com/trained_models/PSMILES_LSTM_1M_5_epochs.pth\n",
    "\n",
    "# getting the pretrained model for LSTM with 42K WDGraph\n",
    "! wget https://deepchemdata.s3.us-west-1.amazonaws.com/trained_models/WDGraph_LSTM_42K_50_epochs.pth"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7f87eaf8-8278-4754-858c-e156eac92051",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<All keys matched successfully>"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#PSMILES Generation\n",
    "generator = LSTMGenerator()\n",
    "ckpt = torch.load(\"./PSMILES_LSTM_1M_5_epochs.pth\")\n",
    "generator.model.load_state_dict(ckpt)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "5b1b38d1",
   "metadata": {},
   "outputs": [],
   "source": [
    "psmile_samples = generator.sample(20, max_len = 500)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "f8224e72",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 . [*]OC(=O)c1nc(-c2ccccc2)[nH]c1-c1ccc(S(=O)(=O)c2ccc(C([*])=O)cc2)cc1\n",
      "2 . [*]C(=O)C(Cc1cccc(C2CC(C)(C)CC(C)(c3ccc4c(c3)C(=O)N([*])C4=O)C2)c1)(C(F)(F)F)C(F)(F)F\n",
      "3 . [*]c1ccc2c(c1)nc([*])n2-c1ccc(-c2cccc(C3(C)CC(c4cccc(N5C(=O)c6ccc(C(=O)O)cc6C5=O)c4)CC(C)(C)C3)c2)cc1\n",
      "4 . [*]c1ccc2c(c1)C(=O)N(c1ccc(N3C(=O)c4ccc(-c5ccc(C)c(-c6ccc7c(c6)C(=O)N([*])C7=O)c5)cc4C3=O)c(N3C(=O)c4ccccc4C3=O)c1)C2=O\n",
      "5 . [*]CSC(=O)CCCCCCCCC(=O)N1C(=O)c2ccc([Si](C)(C)c3ccc(-c4ccc([*])cc4)cc3)cc2C1=O\n",
      "6 . [*]c1nc2cc(Nc3ccc4c(c3)C(=O)N([*])C4=O)ccc2nc1NC=Nc1ccc(N=Cc2ccccc2NC(=O)CCCCCCCCCC(=O)OC)cc1\n",
      "7 . [*]c1ccc(Oc2ccc([*])cc2-c2nnc(-c3ccccc3)o2)s1\n",
      "8 . [*]CCCC(=O)Nc1ccc(N=Cc2ccc(-c3ccc(N=Nc4ccc(CCC[*])cc4)cc3)cc2)cc1\n",
      "9 . [*]c1ccc(-c2ccc(-c3ccc(C)cc3-c3ccc4c(c3)C(=O)N(c3cccc(-c5cc([*])c6cccccc5-6)c3)C4=O)cc2)cc1\n",
      "10 . [*]c1ccc(C#Cc2ccc(Oc3ccc([*])cc3-c3ccc(S(=O)(=O)c4ccc(-c5ccc(C#N)cc5)cc4)cc3)cc2)cc1\n",
      "11 . [*]Oc1ccc(C(O)c2ccc([*])cc2)c(-c2ccccc2)c1\n",
      "12 . [*]CCCCCCCCCCCCOC(=O)c1ccc(-c2ccc([Si](C)(C)c3ccc(N4C(=O)c5cc6c(cc5C4=O)C4(C(C)C)c4ccccc4C4=C5OC(=O)C(CCC[*])C5C4)cc3)cc2)cc1\n",
      "13 . [*]NNC(=O)c1ccc(C(C)(C)c2cc(-c3ccc([*])cc3)cc(C(C)(C)C)c2)cc1\n",
      "14 . [*]CCCc1ccc(-c2ccc(-c3cc(CCC[*])ccc3C(=O)c3ccccc3)s2)cc1\n",
      "15 . [*]OS(=O)(=O)c1ccc([Si](C)(C)c2ccc(-c3ccc(-c4ccc(S(=O)(=O)OC5(OS(=O)(=O)c6ccc(Oc7ccc([*])cc7)cc6)CC6CCC5C6)c5ccccc45)cc3)cc2)cc1\n",
      "16 . [*]c1ccc2c(c1)C(=O)N(c1ccc(C(C)=Nc3ccc(ON4Cc5ccc(ON6Cc7ccc(C(C)(C)c8ccc([*])cc8)cc7C6=O)cc5C4=O)cc3)cc1)C2=O\n",
      "17 . [*]NC1CC(C)(C)CC(C)(c2ccc(Oc3ccc(C=Nc4ccc(-c5ccc(C=Nc6ccc(-c7cccc([*])c7C)cc6)cc5)cc4)cc3)cc2)C1=O\n",
      "18 . [*]OCCSSc1ccc(CSC)c(C([*])=O)c1\n",
      "19 . [*]c1cccc(-c2ccc(-c3cc(NCc4ccccc4)c([*])s3)cc2)c1\n",
      "20 . [*]CN1C(=O)c2ccc(-c3ccc(C4CCC(c5ccc(N6C(=O)c7ccc(O[*])cc7C6=O)c(C)c5)C(C)C4)cc3C)cc2C1=O\n"
     ]
    }
   ],
   "source": [
    "for i, candidate in enumerate(psmile_samples):\n",
    "    print(i+1,\".\", candidate)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ad63afed",
   "metadata": {},
   "source": [
    "In above manner, we can use the pretrained checkpoint from the research paper to generate hypotheical PSMILES candidates. Yet not all candidates are chemically valid, hence we can employ a validation setup as follows."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "8b0c264c",
   "metadata": {},
   "outputs": [],
   "source": [
    "def validate_psmiles(smiles: str) -> bool:\n",
    "    try:\n",
    "        mol = Chem.MolFromSmiles(smiles)\n",
    "        return mol is not None\n",
    "    except:\n",
    "        return False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "8da4b5b2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "value >> [*]OC(=O)c1nc(-c2ccccc2)[nH]c1-c1ccc(S(=O)(=O)c2ccc(C([*])=O)cc2)cc1\n",
      "validation result >> True\n",
      "value >> [*]C(=O)C(Cc1cccc(C2CC(C)(C)CC(C)(c3ccc4c(c3)C(=O)N([*])C4=O)C2)c1)(C(F)(F)F)C(F)(F)F\n",
      "validation result >> True\n",
      "value >> [*]c1ccc2c(c1)nc([*])n2-c1ccc(-c2cccc(C3(C)CC(c4cccc(N5C(=O)c6ccc(C(=O)O)cc6C5=O)c4)CC(C)(C)C3)c2)cc1\n",
      "validation result >> True\n",
      "value >> [*]c1ccc2c(c1)C(=O)N(c1ccc(N3C(=O)c4ccc(-c5ccc(C)c(-c6ccc7c(c6)C(=O)N([*])C7=O)c5)cc4C3=O)c(N3C(=O)c4ccccc4C3=O)c1)C2=O\n",
      "validation result >> True\n",
      "value >> [*]CSC(=O)CCCCCCCCC(=O)N1C(=O)c2ccc([Si](C)(C)c3ccc(-c4ccc([*])cc4)cc3)cc2C1=O\n",
      "validation result >> True\n",
      "value >> [*]c1nc2cc(Nc3ccc4c(c3)C(=O)N([*])C4=O)ccc2nc1NC=Nc1ccc(N=Cc2ccccc2NC(=O)CCCCCCCCCC(=O)OC)cc1\n",
      "validation result >> True\n",
      "value >> [*]c1ccc(Oc2ccc([*])cc2-c2nnc(-c3ccccc3)o2)s1\n",
      "validation result >> True\n",
      "value >> [*]CCCC(=O)Nc1ccc(N=Cc2ccc(-c3ccc(N=Nc4ccc(CCC[*])cc4)cc3)cc2)cc1\n",
      "validation result >> True\n",
      "value >> [*]c1ccc(-c2ccc(-c3ccc(C)cc3-c3ccc4c(c3)C(=O)N(c3cccc(-c5cc([*])c6cccccc5-6)c3)C4=O)cc2)cc1\n",
      "validation result >> True\n",
      "value >> [*]c1ccc(C#Cc2ccc(Oc3ccc([*])cc3-c3ccc(S(=O)(=O)c4ccc(-c5ccc(C#N)cc5)cc4)cc3)cc2)cc1\n",
      "validation result >> True\n",
      "value >> [*]Oc1ccc(C(O)c2ccc([*])cc2)c(-c2ccccc2)c1\n",
      "validation result >> True\n",
      "value >> [*]CCCCCCCCCCCCOC(=O)c1ccc(-c2ccc([Si](C)(C)c3ccc(N4C(=O)c5cc6c(cc5C4=O)C4(C(C)C)c4ccccc4C4=C5OC(=O)C(CCC[*])C5C4)cc3)cc2)cc1\n",
      "validation result >> False\n",
      "value >> [*]NNC(=O)c1ccc(C(C)(C)c2cc(-c3ccc([*])cc3)cc(C(C)(C)C)c2)cc1\n",
      "validation result >> True\n",
      "value >> [*]CCCc1ccc(-c2ccc(-c3cc(CCC[*])ccc3C(=O)c3ccccc3)s2)cc1\n",
      "validation result >> True\n",
      "value >> [*]OS(=O)(=O)c1ccc([Si](C)(C)c2ccc(-c3ccc(-c4ccc(S(=O)(=O)OC5(OS(=O)(=O)c6ccc(Oc7ccc([*])cc7)cc6)CC6CCC5C6)c5ccccc45)cc3)cc2)cc1\n",
      "validation result >> True\n",
      "value >> [*]c1ccc2c(c1)C(=O)N(c1ccc(C(C)=Nc3ccc(ON4Cc5ccc(ON6Cc7ccc(C(C)(C)c8ccc([*])cc8)cc7C6=O)cc5C4=O)cc3)cc1)C2=O\n",
      "validation result >> True\n",
      "value >> [*]NC1CC(C)(C)CC(C)(c2ccc(Oc3ccc(C=Nc4ccc(-c5ccc(C=Nc6ccc(-c7cccc([*])c7C)cc6)cc5)cc4)cc3)cc2)C1=O\n",
      "validation result >> True\n",
      "value >> [*]OCCSSc1ccc(CSC)c(C([*])=O)c1\n",
      "validation result >> True\n",
      "value >> [*]c1cccc(-c2ccc(-c3cc(NCc4ccccc4)c([*])s3)cc2)c1\n",
      "validation result >> True\n",
      "value >> [*]CN1C(=O)c2ccc(-c3ccc(C4CCC(c5ccc(N6C(=O)c7ccc(O[*])cc7C6=O)c(C)c5)C(C)C4)cc3C)cc2C1=O\n",
      "validation result >> True\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[21:26:07] SMILES Parse Error: ring closure 4 duplicates bond between atom 42 and atom 46 for input: '[At]CCCCCCCCCCCCOC(=O)c1ccc(-c2ccc([Si](C)(C)c3ccc(N4C(=O)c5cc6c(cc5C4=O)C4(C(C)C)c4ccccc4C4=C5OC(=O)C(CCC[At])C5C4)cc3)cc2)cc1'\n"
     ]
    }
   ],
   "source": [
    "for i, psmiles in enumerate(psmile_samples):\n",
    "    print(\"value >>\", psmiles)\n",
    "    psmiles = psmiles.replace(\"[*]\", \"[At]\")\n",
    "    print(\"validation result >>\", validate_psmiles(psmiles))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "acb99a39",
   "metadata": {},
   "source": [
    "We can observe 1 of the generated PSMILES are invalid out of 20 generations."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d2c0efa5",
   "metadata": {},
   "source": [
    "In similar fashion, we can use LSTM for weighted directed graph representation strings as follows."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "422f68ef",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<All keys matched successfully>"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "generator_wdg = LSTMGenerator()\n",
    "ckpt = torch.load(\"./WDGraph_LSTM_42K_50_epochs.pth\")\n",
    "generator_wdg.model.load_state_dict(ckpt)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "76cee24d",
   "metadata": {},
   "outputs": [],
   "source": [
    "wdg_gen_values = generator_wdg.sample(20, max_len = 500)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "991bc296",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 . [*:1]c1ccc2c(c1)S(=O)(=O)c1cc([*:2])ccc1-2.[*:3]c1ncc([*:4])c2ccccc12|0.25|0.75|<1-3:0.25:0.25<1-4:0.25:0.25<2-3:0.25:0.25<2-4:0.25:0.25<1-2:0.25:0.25<3-4:0.25:0.25<1-1:0.25:0.25<2-2:0.25:0.25<3-3:0.25:0.25<4-4:0.25:0.25\n",
      "2 . [*:1]c1ccc2c(c1)S(=O)(=O)c1cc([*:2])ccc1-2.[*:3]c1sc([*:4])c2c1OCCO2|0.25|0.75|<1-3:0.25:0.25<1-4:0.25:0.25<2-3:0.25:0.25<2-4:0.25:0.25<1-2:0.25:0.25<3-4:0.25:0.25<1-1:0.25:0.25<2-2:0.25:0.25<3-3:0.25:0.25<4-4:0.25:0.25\n",
      "3 . [*:1]c1ccc2c(c1)S(=O)(=O)c1cc([*:2])ccc1-2.[*:3]c1cc([*:4])cc(CC(=O)O)c1|0.25|0.75|<1-3:0.25:0.25<1-4:0.25:0.25<2-3:0.25:0.25<2-4:0.25:0.25<1-2:0.25:0.25<3-4:0.25:0.25<1-1:0.25:0.25<2-2:0.25:0.25<3-3:0.25:0.25<4-4:0.25:0.25\n",
      "4 . [*:1]c1ccc2c(c1)S(=O)(=O)c1cc([*:2])ccc1-2.[*:3]c1ccc([*:4])c(Cl)c1Cl|0.25|0.75|<1-2:0.375:0.375<1-1:0.375:0.375<2-2:0.375:0.375<3-4:0.375:0.375<3-3:0.375:0.375<4-4:0.125:0.125<1-3:0.125:0.125<1-4:0.125:0.125<2-3:0.125:0.125<2-4:0.125:0.125\n",
      "5 . [*:1]c1ccc2c(c1)S(=O)(=O)c1cc([*:2])ccc1-2.[*:3]c1sc([*:4])c(C(=O)O)c1C(=O)O|0.25|0.75|<1-2:0.375:0.375<1-1:0.375:0.375<2-2:0.375:0.375<3-4:0.375:0.375<3-3:0.375:0.375<4-4:0.125:0.125<1-3:0.125:0.125<1-4:0.125:0.125<2-3:0.125:0.125<2-4:0.125:0.125\n",
      "6 . [*:1]c1ccc2c(c1)S(=O)(=O)c1cc([*:2])ccc1-2.[*:3]c1ccc(C)c([*:4])n1|0.25|0.75|<1-2:0.375:0.375<1-1:0.375:0.375<2-2:0.375:0.375<3-4:0.375:0.375<3-3:0.375:0.375<4-4:0.125:0.125<1-3:0.125:0.125<1-4:0.125:0.125<2-3:0.125:0.125<2-4:0.125:0.125\n",
      "7 . [*:1]c1ccc2c(c1)S(=O)(=O)c1cc([*:2])ccc1-2.[*:3]c1cnc([*:4])cn1|0.25|0.75|<1-2:0.375:0.375<1-1:0.375:0.375<2-2:0.375:0.375<3-4:0.375:0.375<3-3:0.375:0.375<4-4:0.125:0.125<1-3:0.125:0.125<1-4:0.125:0.125<2-3:0.125:0.125<2-4:0.125:0.125\n",
      "8 . [*:1]c1cc2cc3sc([*:2])cc3cc2s1.[*:3]c1cc([*:4])c(N)c(C(=O)O)c1F|0.25|0.75|<1-2:0.375:0.375<1-1:0.375:0.375<2-2:0.375:0.375<3-4:0.375:0.375<3-3:0.375:0.375<4-4:0.125:0.125<1-3:0.125:0.125<1-4:0.125:0.125<2-3:0.125:0.125<2-4:0.125:0.125\n",
      "9 . [*:1]c1ccc2c(c1)S(=O)(=O)c1cc([*:2])ccc1-2.[*:3]c1ccnc2ccc([*:4])cc12|0.25|0.75|<1-3:0.25:0.25<1-4:0.25:0.25<2-3:0.25:0.25<2-4:0.25:0.25<1-2:0.25:0.25<3-4:0.25:0.25<1-1:0.25:0.25<2-2:0.25:0.25<3-3:0.25:0.25<4-4:0.25:0.25\n",
      "10 . [*:1]c1ccc2c(c1)S(=O)(=O)c1cc([*:2])ccc1-2.[*:3]c1cc([*:4])cc(C(=O)O)c1|0.25|0.75|<1-2:0.375:0.375<1-1:0.375:0.375<2-2:0.375:0.375<3-4:0.375:0.375<3-3:0.375:0.375<4-4:0.125:0.125<1-3:0.125:0.125<1-4:0.125:0.125<2-3:0.125:0.125<2-4:0.125:0.125\n",
      "11 . [*:1]c1ccc2c(c1)S(=O)(=O)c1cc([*:2])ccc1-2.[*:3]c1cnc(OC)c([*:4])c1OC|0.25|0.75|<1-3:0.25:0.25<1-4:0.25:0.25<2-3:0.25:0.25<2-4:0.25:0.25<1-2:0.25:0.25<3-4:0.25:0.25<1-1:0.25:0.25<2-2:0.25:0.25<3-3:0.25:0.25<4-4:0.25:0.25\n",
      "12 . [*:1]c1ccc2c(c1)S(=O)(=O)c1cc([*:2])ccc1-2.[*:3]c1cc([*:4])cc(C(N)=S)c1|0.25|0.75|<1-2:0.375:0.375<1-1:0.375:0.375<2-2:0.375:0.375<3-4:0.375:0.375<3-3:0.375:0.375<4-4:0.125:0.125<1-3:0.125:0.125<1-4:0.125:0.125<2-3:0.125:0.125<2-4:0.125:0.125\n",
      "13 . [*:1]c1ccc2c(c1)S(=O)(=O)c1cc([*:2])ccc1-2.[*:3]c1ccc([*:4])c2ccccc12|0.25|0.75|<1-3:0.25:0.25<1-4:0.25:0.25<2-3:0.25:0.25<2-4:0.25:0.25<1-2:0.25:0.25<3-4:0.25:0.25<1-1:0.25:0.25<2-2:0.25:0.25<3-3:0.25:0.25<4-4:0.25:0.25\n",
      "14 . [*:1]c1ccc2c(c1)S(=O)(=O)c1cc([*:2])ccc1-2.[*:3]c1ccc(C(=O)O)c([*:4])n1|0.25|0.75|<1-2:0.375:0.375<1-1:0.375:0.375<2-2:0.375:0.375<3-4:0.375:0.375<3-3:0.375:0.375<4-4:0.125:0.125<1-3:0.125:0.125<1-4:0.125:0.125<2-3:0.125:0.125<2-4:0.125:0.125\n",
      "15 . [*:1]c1ccc2c(c1)S(=O)(=O)c1cc([*:2])ccc1-2.[*:3]c1ccc(C(=O)O)c([*:4])c1|0.25|0.75|<1-3:0.25:0.25<1-4:0.25:0.25<2-3:0.25:0.25<2-4:0.25:0.25<1-2:0.25:0.25<3-4:0.25:0.25<1-1:0.25:0.25<2-2:0.25:0.25<3-3:0.25:0.25<4-4:0.25:0.25\n",
      "16 . [*:1]c1ccc2c(c1)S(=O)(=O)c1cc([*:2])ccc1-2.[*:3]c1ccc(-c2ccc(-c3ccc([*:4])s3)s2)s1|0.25|0.75|<1-3:0.25:0.25<1-4:0.25:0.25<2-3:0.25:0.25<2-4:0.25:0.25<1-2:0.25:0.25<3-4:0.25:0.25<1-1:0.25:0.25<2-2:0.25:0.25<3-3:0.25:0.25<4-4:0.25:0.25\n",
      "17 . [*:1]c1ccc2c(c1)S(=O)(=O)c1cc([*:2])ccc1-2.[*:3]c1cc(Cl)cc([*:4])c1O|0.25|0.75|<1-2:0.375:0.375<1-1:0.375:0.375<2-2:0.375:0.375<3-4:0.375:0.375<3-3:0.375:0.375<4-4:0.125:0.125<1-3:0.125:0.125<1-4:0.125:0.125<2-3:0.125:0.125<2-4:0.125:0.125\n",
      "18 . [*:1]c1ccc2c(c1)S(=O)(=O)c1cc([*:2])ccc1-2.[*:3]c1cc([*:4])cnc1C|0.25|0.75|<1-2:0.375:0.375<1-1:0.375:0.375<2-2:0.375:0.375<3-4:0.375:0.375<3-3:0.375:0.375<4-4:0.125:0.125<1-3:0.125:0.125<1-4:0.125:0.125<2-3:0.125:0.125<2-4:0.125:0.125\n",
      "19 . [*:1]c1ccc2c(c1)S(=O)(=O)c1cc([*:2])ccc1-2.[*:3]c1cc(C)c2ccc([*:4])ccc12|0.25|0.75|<1-3:0.25:0.25<1-4:0.25:0.25<2-3:0.25:0.25<2-4:0.25:0.25<1-2:0.25:0.25<3-4:0.25:0.25<1-1:0.25:0.25<2-2:0.25:0.25<3-3:0.25:0.25<4-4:0.25:0.25\n",
      "20 . [*:1]c1ccc2c(c1)S(=O)(=O)c1cc([*:2])ccc1-2.[*:3]c1cc([*:4])c(OC)c(C(=O)O)c1OC|0.25|0.75|<1-2:0.375:0.375<1-1:0.375:0.375<2-2:0.375:0.375<3-4:0.375:0.375<3-3:0.375:0.375<4-4:0.125:0.125<1-3:0.125:0.125<1-4:0.125:0.125<2-3:0.125:0.125<2-4:0.125:0.125\n"
     ]
    }
   ],
   "source": [
    "for i, candidate in enumerate(wdg_gen_values):\n",
    "    print(i+1,\".\", candidate)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4d8922ef",
   "metadata": {},
   "source": [
    "In this manner 20 candidates with weighted directed graph string format have been generated. We can employ a validation workflow for the same. The validation for the weighted directed graphs is implemented in deepchem with `PolyWDGStringValidator` class. The validator class is specifically aligned with a slightly different format for it's ease of featurization with graph based neural networks. The difference is as follows.\n",
    "\n",
    "The generated format -> ``[*:1]c1ccc2c(c1)S(=O)(=O)c1cc([*:2])ccc1-2.[*:3]c1cc2cc3sc([*:4])cc3c2n1``\n",
    "\n",
    "\n",
    "The compatible format -> ``[1*]c1ccc2c(c1)S(=O)(=O)c1cc([2*])ccc1-2.[3*]c1cc2cc3sc([4*])cc3c2n1``"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8a236dec",
   "metadata": {},
   "source": [
    "To employ a simple conversion of these formats we will utilize regular expression as follows."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "4a84e734",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "raw format >> [*:1]c1ccc2c(c1)S(=O)(=O)c1cc([*:2])ccc1-2.[*:3]c1ncc([*:4])c2ccccc12|0.25|0.75|<1-3:0.25:0.25<1-4:0.25:0.25<2-3:0.25:0.25<2-4:0.25:0.25<1-2:0.25:0.25<3-4:0.25:0.25<1-1:0.25:0.25<2-2:0.25:0.25<3-3:0.25:0.25<4-4:0.25:0.25\n",
      "converted_format >> [1*]c1ccc2c(c1)S(=O)(=O)c1cc([2*])ccc1-2.[3*]c1ncc([4*])c2ccccc12|0.25|0.75|<1-3:0.25:0.25<1-4:0.25:0.25<2-3:0.25:0.25<2-4:0.25:0.25<1-2:0.25:0.25<3-4:0.25:0.25<1-1:0.25:0.25<2-2:0.25:0.25<3-3:0.25:0.25<4-4:0.25:0.25\n"
     ]
    }
   ],
   "source": [
    "import re\n",
    "\n",
    "def convert_format(text):\n",
    "    return re.sub(r'\\[\\*\\:(\\d+)\\]', r'[\\1*]', text)\n",
    "\n",
    "converted_values = [convert_format(value) for value in wdg_gen_values]\n",
    "print(\"raw format >>\", wdg_gen_values[0])\n",
    "print(\"converted_format >>\",converted_values[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "80eacc1d",
   "metadata": {},
   "source": [
    "We can observe in the above outputs that the format is slightly changed to align with the validator. In the code below we are imposing validation on converted candidates."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "id": "06d90cf5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "value >> [1*]c1ccc2c(c1)S(=O)(=O)c1cc([2*])ccc1-2.[3*]c1ncc([4*])c2ccccc12|0.25|0.75|<1-3:0.25:0.25<1-4:0.25:0.25<2-3:0.25:0.25<2-4:0.25:0.25<1-2:0.25:0.25<3-4:0.25:0.25<1-1:0.25:0.25<2-2:0.25:0.25<3-3:0.25:0.25<4-4:0.25:0.25\n",
      "validation result >> True\n",
      "value >> [1*]c1ccc2c(c1)S(=O)(=O)c1cc([2*])ccc1-2.[3*]c1sc([4*])c2c1OCCO2|0.25|0.75|<1-3:0.25:0.25<1-4:0.25:0.25<2-3:0.25:0.25<2-4:0.25:0.25<1-2:0.25:0.25<3-4:0.25:0.25<1-1:0.25:0.25<2-2:0.25:0.25<3-3:0.25:0.25<4-4:0.25:0.25\n",
      "validation result >> True\n",
      "value >> [1*]c1ccc2c(c1)S(=O)(=O)c1cc([2*])ccc1-2.[3*]c1cc([4*])cc(CC(=O)O)c1|0.25|0.75|<1-3:0.25:0.25<1-4:0.25:0.25<2-3:0.25:0.25<2-4:0.25:0.25<1-2:0.25:0.25<3-4:0.25:0.25<1-1:0.25:0.25<2-2:0.25:0.25<3-3:0.25:0.25<4-4:0.25:0.25\n",
      "validation result >> True\n",
      "value >> [1*]c1ccc2c(c1)S(=O)(=O)c1cc([2*])ccc1-2.[3*]c1ccc([4*])c(Cl)c1Cl|0.25|0.75|<1-2:0.375:0.375<1-1:0.375:0.375<2-2:0.375:0.375<3-4:0.375:0.375<3-3:0.375:0.375<4-4:0.125:0.125<1-3:0.125:0.125<1-4:0.125:0.125<2-3:0.125:0.125<2-4:0.125:0.125\n",
      "validation result >> True\n",
      "value >> [1*]c1ccc2c(c1)S(=O)(=O)c1cc([2*])ccc1-2.[3*]c1sc([4*])c(C(=O)O)c1C(=O)O|0.25|0.75|<1-2:0.375:0.375<1-1:0.375:0.375<2-2:0.375:0.375<3-4:0.375:0.375<3-3:0.375:0.375<4-4:0.125:0.125<1-3:0.125:0.125<1-4:0.125:0.125<2-3:0.125:0.125<2-4:0.125:0.125\n",
      "validation result >> True\n",
      "value >> [1*]c1ccc2c(c1)S(=O)(=O)c1cc([2*])ccc1-2.[3*]c1ccc(C)c([4*])n1|0.25|0.75|<1-2:0.375:0.375<1-1:0.375:0.375<2-2:0.375:0.375<3-4:0.375:0.375<3-3:0.375:0.375<4-4:0.125:0.125<1-3:0.125:0.125<1-4:0.125:0.125<2-3:0.125:0.125<2-4:0.125:0.125\n",
      "validation result >> True\n",
      "value >> [1*]c1ccc2c(c1)S(=O)(=O)c1cc([2*])ccc1-2.[3*]c1cnc([4*])cn1|0.25|0.75|<1-2:0.375:0.375<1-1:0.375:0.375<2-2:0.375:0.375<3-4:0.375:0.375<3-3:0.375:0.375<4-4:0.125:0.125<1-3:0.125:0.125<1-4:0.125:0.125<2-3:0.125:0.125<2-4:0.125:0.125\n",
      "validation result >> True\n",
      "value >> [1*]c1cc2cc3sc([2*])cc3cc2s1.[3*]c1cc([4*])c(N)c(C(=O)O)c1F|0.25|0.75|<1-2:0.375:0.375<1-1:0.375:0.375<2-2:0.375:0.375<3-4:0.375:0.375<3-3:0.375:0.375<4-4:0.125:0.125<1-3:0.125:0.125<1-4:0.125:0.125<2-3:0.125:0.125<2-4:0.125:0.125\n",
      "validation result >> True\n",
      "value >> [1*]c1ccc2c(c1)S(=O)(=O)c1cc([2*])ccc1-2.[3*]c1ccnc2ccc([4*])cc12|0.25|0.75|<1-3:0.25:0.25<1-4:0.25:0.25<2-3:0.25:0.25<2-4:0.25:0.25<1-2:0.25:0.25<3-4:0.25:0.25<1-1:0.25:0.25<2-2:0.25:0.25<3-3:0.25:0.25<4-4:0.25:0.25\n",
      "validation result >> True\n",
      "value >> [1*]c1ccc2c(c1)S(=O)(=O)c1cc([2*])ccc1-2.[3*]c1cc([4*])cc(C(=O)O)c1|0.25|0.75|<1-2:0.375:0.375<1-1:0.375:0.375<2-2:0.375:0.375<3-4:0.375:0.375<3-3:0.375:0.375<4-4:0.125:0.125<1-3:0.125:0.125<1-4:0.125:0.125<2-3:0.125:0.125<2-4:0.125:0.125\n",
      "validation result >> True\n",
      "value >> [1*]c1ccc2c(c1)S(=O)(=O)c1cc([2*])ccc1-2.[3*]c1cnc(OC)c([4*])c1OC|0.25|0.75|<1-3:0.25:0.25<1-4:0.25:0.25<2-3:0.25:0.25<2-4:0.25:0.25<1-2:0.25:0.25<3-4:0.25:0.25<1-1:0.25:0.25<2-2:0.25:0.25<3-3:0.25:0.25<4-4:0.25:0.25\n",
      "validation result >> True\n",
      "value >> [1*]c1ccc2c(c1)S(=O)(=O)c1cc([2*])ccc1-2.[3*]c1cc([4*])cc(C(N)=S)c1|0.25|0.75|<1-2:0.375:0.375<1-1:0.375:0.375<2-2:0.375:0.375<3-4:0.375:0.375<3-3:0.375:0.375<4-4:0.125:0.125<1-3:0.125:0.125<1-4:0.125:0.125<2-3:0.125:0.125<2-4:0.125:0.125\n",
      "validation result >> True\n",
      "value >> [1*]c1ccc2c(c1)S(=O)(=O)c1cc([2*])ccc1-2.[3*]c1ccc([4*])c2ccccc12|0.25|0.75|<1-3:0.25:0.25<1-4:0.25:0.25<2-3:0.25:0.25<2-4:0.25:0.25<1-2:0.25:0.25<3-4:0.25:0.25<1-1:0.25:0.25<2-2:0.25:0.25<3-3:0.25:0.25<4-4:0.25:0.25\n",
      "validation result >> True\n",
      "value >> [1*]c1ccc2c(c1)S(=O)(=O)c1cc([2*])ccc1-2.[3*]c1ccc(C(=O)O)c([4*])n1|0.25|0.75|<1-2:0.375:0.375<1-1:0.375:0.375<2-2:0.375:0.375<3-4:0.375:0.375<3-3:0.375:0.375<4-4:0.125:0.125<1-3:0.125:0.125<1-4:0.125:0.125<2-3:0.125:0.125<2-4:0.125:0.125\n",
      "validation result >> True\n",
      "value >> [1*]c1ccc2c(c1)S(=O)(=O)c1cc([2*])ccc1-2.[3*]c1ccc(C(=O)O)c([4*])c1|0.25|0.75|<1-3:0.25:0.25<1-4:0.25:0.25<2-3:0.25:0.25<2-4:0.25:0.25<1-2:0.25:0.25<3-4:0.25:0.25<1-1:0.25:0.25<2-2:0.25:0.25<3-3:0.25:0.25<4-4:0.25:0.25\n",
      "validation result >> True\n",
      "value >> [1*]c1ccc2c(c1)S(=O)(=O)c1cc([2*])ccc1-2.[3*]c1ccc(-c2ccc(-c3ccc([4*])s3)s2)s1|0.25|0.75|<1-3:0.25:0.25<1-4:0.25:0.25<2-3:0.25:0.25<2-4:0.25:0.25<1-2:0.25:0.25<3-4:0.25:0.25<1-1:0.25:0.25<2-2:0.25:0.25<3-3:0.25:0.25<4-4:0.25:0.25\n",
      "validation result >> True\n",
      "value >> [1*]c1ccc2c(c1)S(=O)(=O)c1cc([2*])ccc1-2.[3*]c1cc(Cl)cc([4*])c1O|0.25|0.75|<1-2:0.375:0.375<1-1:0.375:0.375<2-2:0.375:0.375<3-4:0.375:0.375<3-3:0.375:0.375<4-4:0.125:0.125<1-3:0.125:0.125<1-4:0.125:0.125<2-3:0.125:0.125<2-4:0.125:0.125\n",
      "validation result >> True\n",
      "value >> [1*]c1ccc2c(c1)S(=O)(=O)c1cc([2*])ccc1-2.[3*]c1cc([4*])cnc1C|0.25|0.75|<1-2:0.375:0.375<1-1:0.375:0.375<2-2:0.375:0.375<3-4:0.375:0.375<3-3:0.375:0.375<4-4:0.125:0.125<1-3:0.125:0.125<1-4:0.125:0.125<2-3:0.125:0.125<2-4:0.125:0.125\n",
      "validation result >> True\n",
      "value >> [1*]c1ccc2c(c1)S(=O)(=O)c1cc([2*])ccc1-2.[3*]c1cc(C)c2ccc([4*])ccc12|0.25|0.75|<1-3:0.25:0.25<1-4:0.25:0.25<2-3:0.25:0.25<2-4:0.25:0.25<1-2:0.25:0.25<3-4:0.25:0.25<1-1:0.25:0.25<2-2:0.25:0.25<3-3:0.25:0.25<4-4:0.25:0.25\n",
      "validation result >> True\n",
      "value >> [1*]c1ccc2c(c1)S(=O)(=O)c1cc([2*])ccc1-2.[3*]c1cc([4*])c(OC)c(C(=O)O)c1OC|0.25|0.75|<1-2:0.375:0.375<1-1:0.375:0.375<2-2:0.375:0.375<3-4:0.375:0.375<3-3:0.375:0.375<4-4:0.125:0.125<1-3:0.125:0.125<1-4:0.125:0.125<2-3:0.125:0.125<2-4:0.125:0.125\n",
      "validation result >> True\n"
     ]
    }
   ],
   "source": [
    "validator = PolyWDGStringValidator()\n",
    "for value in converted_values:\n",
    "    print(\"value >>\", value)\n",
    "    print(\"validation result >>\", validator.validate(value))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8c43dbf6",
   "metadata": {},
   "source": [
    "Among the 20 generated candidates, all candidates are found to be valid indicating the effectiveness of the trained LSTM for generating WDG strings."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e314cef2",
   "metadata": {},
   "source": [
    "## 4. Summary <a id=\"sum\"></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4d72fd29",
   "metadata": {},
   "source": [
    "In this tutorial, we explored generative methodologies using the DeepChem library, focusing on BRICS and LSTM-based generators for molecular and polymer generations. We covered the following key points:\n",
    "\n",
    "1. **Introduction to Generative Methods**: We discussed the importance of generative modeling in computational chemistry and introduced BRICS and neural network-based methods like LSTM for molecular generation.\n",
    "\n",
    "2. **BRICS Generation**: We demonstrated how to use the `BRICSGenerator` class in DeepChem to generate novel molecules from SMILES and PSMILES inputs. We also extended the application to dendrimers and polymers.\n",
    "\n",
    "3. **LSTM Generators**: We trained an LSTM model using the `LSTMGenerator` class and generated new molecular sequences. We also showed how to load pretrained models for PSMILES and weighted directed graphs (WDG) to generate new candidates.\n",
    "\n",
    "4. **Validation**: We implemented validation workflows to ensure the chemical validity of generated PSMILES and WDG strings using RDKit and DeepChem's `PolyWDGStringValidator`.\n",
    "\n",
    "By combining rule-based methods like BRICS with deep learning techniques such as LSTM, we can efficiently explore chemical spaces and generate novel molecular structures with potential applications in drug discovery and materials science."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1d9eade9",
   "metadata": {},
   "source": [
    "## 5. Reference <a id=\"ref\"></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "16cd9976",
   "metadata": {},
   "source": [
    "1. [Mohanty, Debasish, et al. \"Open-source Polymer Generative Pipeline.\" arXiv preprint arXiv:2412.08658 (2024).](https://arxiv.org/abs/2412.08658)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "10cfd391",
   "metadata": {},
   "source": [
    "# Congratulations! Time to join the Community!\n",
    "\n",
    "Congratulations on completing this tutorial notebook! If you enjoyed working through the tutorial, and want to continue working with DeepChem, we encourage you to finish the rest of the tutorials in this series. You can also help the DeepChem community in the following ways:\n",
    "\n",
    "## Star DeepChem on [GitHub](https://github.com/deepchem/deepchem)\n",
    "This helps build awareness of the DeepChem project and the tools for open source drug discovery that we're trying to build.\n",
    "\n",
    "## Join the DeepChem Gitter\n",
    "The DeepChem [Gitter](https://gitter.im/deepchem/Lobby) hosts a number of scientists, developers, and enthusiasts interested in deep learning for the life sciences. Join the conversation!\n",
    "\n",
    "## Join the DeepChem Discord\n",
    "The DeepChem [Discord](https://discord.gg/cGzwCdrUqS) hosts a number of scientists, developers, and enthusiasts interested in deep learning for the life sciences. Join the conversation!"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "92599bcf",
   "metadata": {},
   "source": [
    "## Citing The Research Paper\n",
    "This is the implementation of the work mentioned in the research paper \"Open-Source Polymer Generative Pipeline\". If you find this work helpful please consider citing it using provided BibTex. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "99f5dfde",
   "metadata": {},
   "outputs": [],
   "source": [
    "@article{mohanty2024open,\n",
    "  title={Open-source Polymer Generative Pipeline},\n",
    "  author={Mohanty, Debasish and Shreyas, V and Palai, Akshaya and Ramsundar, Bharath},\n",
    "  journal={arXiv preprint arXiv:2412.08658},\n",
    "  year={2024}\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bfcb19fd",
   "metadata": {},
   "source": [
    "## Citing This Tutorial\n",
    "If you found this tutorial useful please consider citing it using the provided BibTeX."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6ef37fa8",
   "metadata": {},
   "outputs": [],
   "source": [
    "@manual{Intro1,\n",
    " title={Exploring Generative Methodologies with Deepchem},\n",
    " organization={DeepChem},\n",
    " author={Mohanty, Debasish},\n",
    " howpublished = {\\url{https://github.com/deepchem/deepchem/blob/master/examples/tutorials/Exploring_Generative_Methodologies_with_Deepchem.ipynb}},\n",
    " year={2025},\n",
    "}"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.20"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
